"""文件操作工具

提供文件和目录管理、路径处理等功能。
"""

import os
import json
import shutil
import hashlib
from typing import List, Dict, Any, Optional, Union, Generator
from pathlib import Path
import datetime


class FileManager:
    """文件管理器
    
    提供文件和目录操作功能。
    """
    
    @staticmethod
    def ensure_dir(path: Union[str, Path]) -> Path:
        """确保目录存在
        
        Args:
            path: 目录路径
            
        Returns:
            目录路径对象
        """
        path = Path(path)
        path.mkdir(parents=True, exist_ok=True)
        return path
    
    @staticmethod
    def get_file_hash(file_path: Union[str, Path], algorithm: str = 'md5') -> str:
        """计算文件哈希值
        
        Args:
            file_path: 文件路径
            algorithm: 哈希算法 (md5, sha1, sha256)
            
        Returns:
            文件哈希值
        """
        hash_func = getattr(hashlib, algorithm.lower())()
        
        with open(file_path, 'rb') as f:
            for chunk in iter(lambda: f.read(4096), b""):
                hash_func.update(chunk)
        
        return hash_func.hexdigest()
    
    @staticmethod
    def get_file_info(file_path: Union[str, Path]) -> Dict[str, Any]:
        """获取文件信息
        
        Args:
            file_path: 文件路径
            
        Returns:
            文件信息字典
        """
        path = Path(file_path)
        
        if not path.exists():
            raise FileNotFoundError(f"文件不存在: {file_path}")
        
        stat = path.stat()
        
        return {
            'path': str(path.absolute()),
            'name': path.name,
            'stem': path.stem,
            'suffix': path.suffix,
            'size': stat.st_size,
            'size_mb': stat.st_size / (1024 * 1024),
            'created_time': datetime.datetime.fromtimestamp(stat.st_ctime),
            'modified_time': datetime.datetime.fromtimestamp(stat.st_mtime),
            'accessed_time': datetime.datetime.fromtimestamp(stat.st_atime),
            'is_file': path.is_file(),
            'is_dir': path.is_dir(),
            'parent': str(path.parent)
        }
    
    @staticmethod
    def list_files(
        directory: Union[str, Path],
        pattern: str = '*',
        recursive: bool = False,
        include_dirs: bool = False
    ) -> List[Path]:
        """列出目录中的文件
        
        Args:
            directory: 目录路径
            pattern: 文件模式
            recursive: 是否递归搜索
            include_dirs: 是否包含目录
            
        Returns:
            文件路径列表
        """
        directory = Path(directory)
        
        if not directory.exists():
            raise FileNotFoundError(f"目录不存在: {directory}")
        
        if recursive:
            paths = directory.rglob(pattern)
        else:
            paths = directory.glob(pattern)
        
        result = []
        for path in paths:
            if path.is_file() or (include_dirs and path.is_dir()):
                result.append(path)
        
        return sorted(result)
    
    @staticmethod
    def copy_file(
        src: Union[str, Path],
        dst: Union[str, Path],
        overwrite: bool = False
    ) -> Path:
        """复制文件
        
        Args:
            src: 源文件路径
            dst: 目标文件路径
            overwrite: 是否覆盖已存在的文件
            
        Returns:
            目标文件路径
        """
        src = Path(src)
        dst = Path(dst)
        
        if not src.exists():
            raise FileNotFoundError(f"源文件不存在: {src}")
        
        if dst.exists() and not overwrite:
            raise FileExistsError(f"目标文件已存在: {dst}")
        
        # 确保目标目录存在
        dst.parent.mkdir(parents=True, exist_ok=True)
        
        shutil.copy2(src, dst)
        return dst
    
    @staticmethod
    def move_file(
        src: Union[str, Path],
        dst: Union[str, Path],
        overwrite: bool = False
    ) -> Path:
        """移动文件
        
        Args:
            src: 源文件路径
            dst: 目标文件路径
            overwrite: 是否覆盖已存在的文件
            
        Returns:
            目标文件路径
        """
        src = Path(src)
        dst = Path(dst)
        
        if not src.exists():
            raise FileNotFoundError(f"源文件不存在: {src}")
        
        if dst.exists() and not overwrite:
            raise FileExistsError(f"目标文件已存在: {dst}")
        
        # 确保目标目录存在
        dst.parent.mkdir(parents=True, exist_ok=True)
        
        shutil.move(str(src), str(dst))
        return dst
    
    @staticmethod
    def delete_file(file_path: Union[str, Path]) -> bool:
        """删除文件
        
        Args:
            file_path: 文件路径
            
        Returns:
            是否删除成功
        """
        try:
            path = Path(file_path)
            if path.exists():
                if path.is_file():
                    path.unlink()
                elif path.is_dir():
                    shutil.rmtree(path)
                return True
            return False
        except Exception:
            return False
    
    @staticmethod
    def clean_directory(
        directory: Union[str, Path],
        keep_dirs: bool = True,
        pattern: str = '*'
    ) -> int:
        """清理目录
        
        Args:
            directory: 目录路径
            keep_dirs: 是否保留子目录
            pattern: 要删除的文件模式
            
        Returns:
            删除的文件数量
        """
        directory = Path(directory)
        
        if not directory.exists():
            return 0
        
        deleted_count = 0
        
        for item in directory.glob(pattern):
            try:
                if item.is_file():
                    item.unlink()
                    deleted_count += 1
                elif item.is_dir() and not keep_dirs:
                    shutil.rmtree(item)
                    deleted_count += 1
            except Exception:
                continue
        
        return deleted_count
    
    @staticmethod
    def get_directory_size(directory: Union[str, Path]) -> Dict[str, Any]:
        """获取目录大小
        
        Args:
            directory: 目录路径
            
        Returns:
            目录大小信息
        """
        directory = Path(directory)
        
        if not directory.exists():
            raise FileNotFoundError(f"目录不存在: {directory}")
        
        total_size = 0
        file_count = 0
        dir_count = 0
        
        for item in directory.rglob('*'):
            if item.is_file():
                try:
                    total_size += item.stat().st_size
                    file_count += 1
                except Exception:
                    continue
            elif item.is_dir():
                dir_count += 1
        
        return {
            'total_size_bytes': total_size,
            'total_size_mb': total_size / (1024 * 1024),
            'total_size_gb': total_size / (1024 * 1024 * 1024),
            'file_count': file_count,
            'dir_count': dir_count,
            'path': str(directory.absolute())
        }


class ConfigManager:
    """配置管理器
    
    提供配置文件的读写和管理功能。
    """
    
    def __init__(self, config_path: Union[str, Path]):
        """初始化配置管理器
        
        Args:
            config_path: 配置文件路径
        """
        self.config_path = Path(config_path)
        self._config_data = {}
        self._load_config()
    
    def _load_config(self):
        """加载配置文件"""
        if self.config_path.exists():
            try:
                with open(self.config_path, 'r', encoding='utf-8') as f:
                    if self.config_path.suffix.lower() == '.json':
                        self._config_data = json.load(f)
                    elif self.config_path.suffix.lower() in ['.yml', '.yaml']:
                        import yaml
                        self._config_data = yaml.safe_load(f)
                    else:
                        raise ValueError(f"不支持的配置文件格式: {self.config_path.suffix}")
            except Exception as e:
                print(f"加载配置文件失败: {e}")
                self._config_data = {}
        else:
            self._config_data = {}
    
    def save_config(self):
        """保存配置文件"""
        # 确保目录存在
        self.config_path.parent.mkdir(parents=True, exist_ok=True)
        
        try:
            with open(self.config_path, 'w', encoding='utf-8') as f:
                if self.config_path.suffix.lower() == '.json':
                    json.dump(self._config_data, f, indent=2, ensure_ascii=False)
                elif self.config_path.suffix.lower() in ['.yml', '.yaml']:
                    import yaml
                    yaml.dump(self._config_data, f, default_flow_style=False, 
                             allow_unicode=True, indent=2)
                else:
                    raise ValueError(f"不支持的配置文件格式: {self.config_path.suffix}")
        except Exception as e:
            print(f"保存配置文件失败: {e}")
    
    def get(self, key: str, default: Any = None) -> Any:
        """获取配置值
        
        Args:
            key: 配置键（支持点分隔的嵌套键）
            default: 默认值
            
        Returns:
            配置值
        """
        keys = key.split('.')
        value = self._config_data
        
        try:
            for k in keys:
                value = value[k]
            return value
        except (KeyError, TypeError):
            return default
    
    def set(self, key: str, value: Any):
        """设置配置值
        
        Args:
            key: 配置键（支持点分隔的嵌套键）
            value: 配置值
        """
        keys = key.split('.')
        config = self._config_data
        
        # 创建嵌套字典结构
        for k in keys[:-1]:
            if k not in config or not isinstance(config[k], dict):
                config[k] = {}
            config = config[k]
        
        config[keys[-1]] = value
    
    def update(self, data: Dict[str, Any]):
        """更新配置数据
        
        Args:
            data: 配置数据字典
        """
        def deep_update(base_dict, update_dict):
            for key, value in update_dict.items():
                if key in base_dict and isinstance(base_dict[key], dict) and isinstance(value, dict):
                    deep_update(base_dict[key], value)
                else:
                    base_dict[key] = value
        
        deep_update(self._config_data, data)
    
    def get_all(self) -> Dict[str, Any]:
        """获取所有配置数据
        
        Returns:
            配置数据字典
        """
        return self._config_data.copy()
    
    def clear(self):
        """清空配置数据"""
        self._config_data.clear()
    
    def has_key(self, key: str) -> bool:
        """检查配置键是否存在
        
        Args:
            key: 配置键
            
        Returns:
            是否存在
        """
        return self.get(key) is not None


def create_unique_filename(
    directory: Union[str, Path],
    base_name: str,
    extension: str = '.txt',
    max_attempts: int = 1000
) -> Path:
    """创建唯一文件名
    
    Args:
        directory: 目录路径
        base_name: 基础文件名
        extension: 文件扩展名
        max_attempts: 最大尝试次数
        
    Returns:
        唯一的文件路径
    """
    directory = Path(directory)
    directory.mkdir(parents=True, exist_ok=True)
    
    # 首先尝试原始文件名
    file_path = directory / f"{base_name}{extension}"
    if not file_path.exists():
        return file_path
    
    # 如果文件已存在，添加数字后缀
    for i in range(1, max_attempts + 1):
        file_path = directory / f"{base_name}_{i:04d}{extension}"
        if not file_path.exists():
            return file_path
    
    raise RuntimeError(f"无法创建唯一文件名，已尝试 {max_attempts} 次")


def backup_file(file_path: Union[str, Path], backup_dir: Optional[Union[str, Path]] = None) -> Path:
    """备份文件
    
    Args:
        file_path: 要备份的文件路径
        backup_dir: 备份目录，None表示在原文件目录下创建备份
        
    Returns:
        备份文件路径
    """
    file_path = Path(file_path)
    
    if not file_path.exists():
        raise FileNotFoundError(f"文件不存在: {file_path}")
    
    # 确定备份目录
    if backup_dir is None:
        backup_dir = file_path.parent
    else:
        backup_dir = Path(backup_dir)
    
    backup_dir.mkdir(parents=True, exist_ok=True)
    
    # 创建备份文件名
    timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
    backup_name = f"{file_path.stem}_backup_{timestamp}{file_path.suffix}"
    backup_path = backup_dir / backup_name
    
    # 复制文件
    shutil.copy2(file_path, backup_path)
    
    return backup_path


def find_files_by_extension(
    directory: Union[str, Path],
    extensions: Union[str, List[str]],
    recursive: bool = True
) -> List[Path]:
    """根据扩展名查找文件
    
    Args:
        directory: 搜索目录
        extensions: 文件扩展名（单个或列表）
        recursive: 是否递归搜索
        
    Returns:
        匹配的文件路径列表
    """
    directory = Path(directory)
    
    if isinstance(extensions, str):
        extensions = [extensions]
    
    # 确保扩展名以点开头
    extensions = [ext if ext.startswith('.') else f'.{ext}' for ext in extensions]
    
    files = []
    
    if recursive:
        pattern_func = directory.rglob
    else:
        pattern_func = directory.glob
    
    for ext in extensions:
        files.extend(pattern_func(f'*{ext}'))
    
    return sorted([f for f in files if f.is_file()])